//
//$Id: islsp_history.h 726 2012-12-14 19:51:50Z andrew.tsyganov $
#pragma once

#include "islsp_solution.h"
//TODO refactor?
#include "solvers/sa/sa_common.h"
#include "solvers/ga/ga_common.h"
#include "solvers/user/user_common.h"

class Islsp_Thread_History
{
public:
    std::vector<Islsp_Solution::TCostType> cost_;
    std::vector< Array<double> > params_;
    std::vector<double> residual_;
};

class Islsp_History
{
    std::vector<Islsp_Thread_History> hist_;
public:
    template<class E>
    bool init(E* env, int np)
    {
        hist_.resize(np);
        return true;
    }

    template<class E, class X>
    void update(int step, int omp_rank, SA<X>& s, E* env)
    {
        Islsp_Solution const& sol = s.get_Current_Solution();

        double residual = 0;
        for (size_t i = 0; i < sol.t_.size(); ++i)
        {
            double tmp = sol.t_[i] - env->problem_.Theta_(i, 0);
            residual += tmp * tmp;
        }
        residual = sqrt(residual);

        hist_[omp_rank].residual_.push_back(residual);
        hist_[omp_rank].cost_.push_back(sol.get_Cost());
        hist_[omp_rank].params_.push_back(sol.t_);
    }

    template<class E, class X>
    void update(int step, int omp_rank, GA<X>& s, E* env)
    {
        Islsp_Solution const& sol = s.get_Best_Solution();

        double residual = 0;
        for (size_t i = 0; i < sol.t_.size(); ++i)
        {
            double tmp = sol.t_[i] - env->problem_.Theta_(i, 0);
            residual += tmp * tmp;
        }
        residual = sqrt(residual);

        hist_[omp_rank].residual_.push_back(residual);
        hist_[omp_rank].cost_.push_back(sol.get_Cost());
        hist_[omp_rank].params_.push_back(sol.t_);
    }

    template<class E, class X>
    void update(int step, int omp_rank, user_solver<X>& s, E& env)
    {
        Islsp_Solution const& sol = s.get_Best_Solution();

        double residual = 0;
        for (size_t i = 0; i < sol.t_.size(); ++i)
        {
            double tmp = sol.t_[i] - env.problem_.Theta_(i, 0);
            residual += tmp * tmp;
        }
        residual = sqrt(residual);

        hist_[omp_rank].residual_.push_back(residual);
        hist_[omp_rank].cost_.push_back(sol.get_Cost());
        hist_[omp_rank].params_.push_back(sol.t_);
    }

    //template<class E, class X>
    //void update(int step, int omp_rank, BNB<X>& s, E& env)
    //{
    //}

    template<class E, class TSolver>
    void output(E* env, TSolver& solver, char const* tech, int np, int mpi_rank = -1)
    {
        //cout << "stat.output" << std::endl;
        if (!env->output_dir_.length())
            return;
        std::string dir = env->output_dir_;
        sys_api::Path_Append(dir, env->cfg_.method_.c_str());
        std::string tmp;
        meta::Value2String(env->rand_seed_, tmp);
        sys_api::Path_Append(dir, tmp.c_str());
        sys_api::Path_Append(dir, "history");
        sys_api::Create_Dir_Recursive(dir.c_str());
        for (size_t i = 0; i < hist_.size(); ++i)
        {
            std::string path = dir;
            std::string name;
            name = solver.get_Name();
            name += ".";
            name += tech;
            name += ".";
            meta::Value2String(np, tmp);
            name += tmp.c_str();
            name += ".";
            if (mpi_rank < 0)
            {
                meta::Value2String(i, tmp);
            }
            else
            {
                ASSERT_(1 == hist_.size());
                meta::Value2String(mpi_rank, tmp);
            }
            name += tmp.c_str();
            name += ".txt";

            sys_api::Path_Append(path, name.c_str());
            output_Ex(path, hist_[i]);
        }
    }

    void output_Ex(std::string const& path, Islsp_Thread_History& hist)
    {
        std::ofstream out(path.c_str(), std::ios::out);
        for (size_t i = 0; i < hist.cost_.size(); ++i)
        {
            out << std::setw(6) << i << std::fixed << std::setw(20) << hist.cost_[i] << std::setw(20) << hist.residual_[i]; 

            /*
            size_t n = hist.params_[i].size();
        
            out << " Theta = (";
            for (size_t j = 0; j < n-1; ++j)
                out << hist.params_[i][j] << "; ";
            out << hist.params_[i][n-1] << ")" << std::endl;
            */

            for (size_t j = 0; j < hist.params_[i].size(); ++j)
                out << std::setw(20) << hist.params_[i][j];

            out << std::endl;
        }
    }

    void enter_Thread(int rank)
    {
    }

    void leave_Thread(int rank)
    {
    }
};

//EOF!
